#include "PDTFunction.h"

PDTFunctionCall::PDTFunctionCall(PDTCodeGraph & CG,
		vector<PDTNode *> & arguments, 
        string & aName, 
        PDTFunctionDec * aFunc) :
    PDTExpBool(CG), name(aName) , func_ptr(aFunc), 
    argc(arguments.size())
   // ,retVar(NULL)
{
    for (int i = 0; i < argc; i++) {
        PDTNode * node = arguments[i];
        addChild(node);
        node->addParent(this);
    }
//    fixReturnVariable(CG);
}

PDTFunctionCall::PDTFunctionCall(PDTCodeGraph & CG,
		string & aName, 
        PDTFunctionDec * aFunc):
	PDTExpBool(CG), name(aName) , func_ptr(aFunc), 
    argc(0)
    //, retVar(NULL)
{
    func_ptr = aFunc;
    argc = 0;
//    fixReturnVariable(CG);
}


bool PDTFunctionCall::checkConsistency(string & msg) {
    if (func_ptr == NULL) {
        msg = "Function call is not resolved";
        return false;
    }

    if (argc != func_ptr->getArgumentCount()) {
        msg = "Mismatch in number of arguments in function call";
        return false;
    }

    if (argc > 0) {
        int i;
        for (i = 0; i < argc; i++) {
            PDTVar* v1 = dynamic_cast<PDTVar*>(children[i]);
            PDTVar* funcV1 = dynamic_cast<PDTVar*>((func_ptr->getChildren())[i]);
            if (!checkVars(v1, funcV1) ){
                break;
            }
        }
        if (i < argc) {
            msg = "Mismatch in variable type";
            return false;
        } else {
            return true;
        }
    } else if (argc == 0) {
        return true;
    } 
    return false;
}
#if 0

void PDTFunctionCall::fixReturnVariable(PDTCodeGraph & CG) 
{
    if (func_ptr == NULL)
        return;
    if (retVar != NULL)
        return;
    PDTNode * varNode = (func_ptr->getReturnVar())->getNode();
    if (varNode->isBoolVar()) {
        PDTBoolVar * boolvar = new PDTBoolVar(CG, getName() + "::ret");
        retVar = (PDTVar*)boolvar;
    } else if (varNode->isNumVar()) {
        PDTNumVar * numvar = new PDTNumVar(CG, getName() + "::ret");
        retVar = (PDTVar*)numvar;
    } else
        return;
    addChild(retVar->getNode());
    retVar->getNode()->addParent(this);
}
#endif

bool PDTFunctionCall::eval(PDTCodeGraph& CG)
{
#if 1
    return true;
#else

    vector<PDTVar*> listOfVar;
    for (unsigned int i = 0; i < func_ptr->getChildren().size() - 2; i++)
    {
        listOfVar.push_back(dynamic_cast<PDTVar*> (func_ptr->getChildren()[i]));
    }

    vector<PDTExp*> listofArg;
    for (unsigned int i = 0; i < func_ptr->getChildren().size() - 2; i++)
    {
        if (children[i]->isExp())
            listofArg.push_back(dynamic_cast<PDTExp*> (children[i]));
    }

    for (unsigned int i = 0; i < func_ptr->getChildren().size() - 2; i++)
    {
        string s = listOfVar[i]->getName();
        if (listOfVar[i]->isSequenceVar())
        {
            PDTSequenceVar* seqVar = dynamic_cast<PDTSequenceVar*> (listOfVar[i]);
            PDTSequenceVar* seqVar2 = dynamic_cast<PDTSequenceVar*> (children[i]);
            PDTSequence* seq = seqVar->getSequence();
            int seqSize = seqVar2->getSequence()->getSeqSize();
            for (int i = 0; i<seqSize; i++)
            {
                PDTNumConstInt ind(CG, i);
                PDTSequenceAccessNum atInd(CG, *(seqVar2->getSequence()) ,ind);
                atInd.eval(CG);
                seq->elements[i] = new PDTNumConstInt(CG, ((PDTNumConstInt*)(atInd.nValue))->getValue());
            }
        }
        else
        {
            PDTAssign assign(CG, *listOfVar[i], *listofArg[i]);
            assign.eval(CG);
        }
    }

    if (func_ptr->getName() == "PDTArraySize")  //Find size of the array
    {
        PDTSequenceVar* seqVar = dynamic_cast<PDTSequenceVar*> (children[0]);
        PDTNumConstInt val(CG, seqVar->getSequence()->getSeqSize());
        PDTAssign assign(CG, *(func_ptr->getReturnVar()), val);
        assign.eval(CG);
    }

    else
    {
        int size = func_ptr->getStatements()->getChildren().size();
        for (int i = 0; i < size; i++)
        {
            PDTStatement * statement = dynamic_cast<PDTStatement*>(func_ptr->getStatements()->getChildren()[i]);
            if (statement->isAssign())
            {
                PDTAssign * assign = dynamic_cast<PDTAssign*> (statement);
                if (assign->getChildren()[1]->isNumVar())
                {
                    PDTNumVar *variable = dynamic_cast<PDTNumVar*> (assign->getChildren()[1]);
                    string s = variable->getName();
                    // This should be fixed when the function call object is an expression 
                    if (s.substr(func_ptr->getName().size()+2, 2) == "rv") //if we reach a function call
                    {
                        string funcCallName = func_ptr->getName();//TODO: something is wrong here
                        PDTNode* funcCall = CG.findFunctionCall(funcCallName);
                        if (funcCallName == func_ptr->getName()) //Here we have a recursive call
                        {
                            PDTFunctionDec* funcClone = (PDTFunctionDec*)(func_ptr->clone(CG));
                            PDTVar* retVarOld = func_ptr->getReturnVar();

                            vector<PDTNode*> listofArg2;
                            for (unsigned int i = 0; i < func_ptr->getChildren().size() - 2; i++)
                            {
                                if (funcCall->getChildren()[i]->isExp())
                                    listofArg2.push_back(funcCall->getChildren()[i]);
                            }

                            string cloneName = funcClone->getName();
                            PDTFunctionCall* funcCallClone = new PDTFunctionCall(CG, listofArg2, cloneName, funcClone);
                            funcCallClone->eval(CG);
                            assign->getChildren()[1] = ((PDTExp*)(funcClone->getReturnVar()->getNode()->nValue));
                        }
                        else
                            funcCall->eval(CG);
                    }
                }
            }
            //Ignore pre/post/behaviors nodes
            if (statement->isPreCondition())
                continue;
            if (statement->isPostCondition())
                continue;
            if (statement->isBehaviors())
                continue;

            if (!statement->eval(CG))
                return false;
        }
        return true;
    }
    return true;
#endif
}


PDTNode* 
PDTFunctionCall::clone(PDTCodeGraph& CG)
{
    PDTNode* toReturn;
    vector <PDTNode*> argumentsClone;
    for (int i = 0; i<argc; i++)
    {
        if (children[i]->isNumVar())
            argumentsClone.push_back((PDTNumVar*)children[i]->clone(CG));
        else if (children[i]->isBoolVar())
            argumentsClone.push_back((PDTBoolVar*)children[i]->clone(CG));
        else if (children[i]->isSequenceVar())
        {
            //TODO
        }
    }
    stringstream sss;
    sss<<cloneID;
    string nameClone = name + "!" + sss.str();
    cloneID++;
    toReturn = new PDTFunctionCall(CG, argumentsClone, nameClone, (PDTFunctionDec*)func_ptr->clone(CG));
    //incrementCloneID();
    //untouch();
    return toReturn;
}

stringstream & 
PDTFunctionCall::toString(stringstream & ss) const {
    assert(isSpecial() || func_ptr != NULL);
    ss << name;
    ss << "( ";
    int size = children.size();
    if (size > 0) {
        for (int i = 0; i < size - 1; i++) {
            children[i]->toString(ss);
            ss << ",";
        }
        children[size - 1]->toString(ss);
    }
    ss << " )";
    ss << endl;
    return ss;
}

bool checkVars(PDTVar * v1, PDTVar * v2) {
	return (v1->getType() == v2->getType());
}

bool PDTFunctionCall::isSpecial(const string & fname) {
    // array special 
    if (fname == "scasize") return true;//array size
    if (fname == "scacat") return true; // concat arrays

    // character special
    if (fname == "scblank") return true; // character is ' '
    if (fname == "scnewline") return true; // 'N'
    if (fname == "scalpha") return true; // a-z
    if (fname == "scnum") return true;   // 0-9
    if (fname == "scalphanum") return true; // scalpha || scnum
    return false;
}

bool PDTFunctionCall::isNum() {
    if (func_ptr != NULL){
        PDTVar * var = func_ptr->getReturnVar();
        if ( var != NULL) {
            return var->getNode()->isNum();
        }
    }
    return false;
}

bool PDTFunctionCall::isBool() {
    if (func_ptr != NULL){
        PDTVar * var = func_ptr->getReturnVar();
        if ( var != NULL) {
            return var->getNode()->isBool();
        }
    }
    return false;
}
